package br.org.bertol.mestrado.util;

import java.io.File;

import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;

import br.org.bertol.mestrado.AbstractStarter;
import br.org.bertol.mestrado.engine.Objective;
import br.org.bertol.mestrado.engine.exception.HeuristicsException;
import br.org.bertol.mestrado.engine.optimisation.hillclimbing.Operador;
import br.org.bertol.mestrado.engine.optimisation.pso.TypeMovement;

/**
 * @author contaqualquer
 */
public class Configurator {

    private final Configuration configuration;

    public Configurator(final String sourceFile) throws ConfigurationException {
        if (sourceFile == null) {
            configuration = new PropertiesConfiguration("pratico.properties");
        } else {
            configuration = new PropertiesConfiguration(sourceFile);
        }
    }

    public Configurator(final File sourceFile) throws ConfigurationException {
        configuration = new PropertiesConfiguration(sourceFile);
    }

    public Configurator() throws ConfigurationException {
        configuration = new PropertiesConfiguration("pratico.properties");
    }

    public final int getNumRestarts() {
        final int input = configuration.getInt("restarts");

        return input;
    }

    public final int getIteracoes() {
        final int input = configuration.getInt("iteracoes");

        return input;
    }

    public final int getDepth() {
        return configuration.getInt("pso.depth");
    }

    public final int getRepositorySize() {
        return configuration.getInt("pso.repositorysize");
    }

    public final float getInitialInertia() {
        final float input = configuration.getFloat("pso.initialinertia");

        return input;
    }

    public final float getFinalInertia() {
        final float input = configuration.getFloat("pso.finalinertia");

        return input;
    }

    public final float getSocialFactor() {
        final float input = configuration.getFloat("pso.fatorsocial");

        return input;
    }

    public final float getCognitivoFactor() {
        final float input = configuration.getFloat("pso.fatorcognitivo");

        return input;
    }

    public final float getConstrictionMultiplicator() {
        final float input = configuration
                .getFloat("pso.multiplicadorconstricao");

        return input;
    }

    /**
     * @return Nº de partículas
     */
    public final int getPopulationSize() {
        return configuration.getInt("pso.populacao");
    }

    public final float getWellThreshoold() {
        return configuration
                .getFloat("ant.wellthreshold");
    }

    public final float getTaxEvaporation() {
        return configuration
                .getFloat("ant.evaporation");
    }

    public final float getFactorAlpha() {
        return configuration
                .getFloat("ant.alpha");
    }

    public final float getFactorBeta() {
        return configuration
                .getFloat("ant.beta");
    }

    /**
     * Algoritmos de formigas.
     * @return float Taxa mínima de feromônios
     */
    public final float getMinPheromone() {
        return configuration
                .getFloat("ant.minpheromone");
    }

    /**
     * @return Array com os sistemsa a serem processados
     */
    public final String[] getSystems() {
        final String[] input = configuration.getStringArray("sistemas");

        if (input.length == 0) {
            return input;
        }

        final String[] systems = new String[input.length];

        for (int i = 0; i < input.length; i++) {
            systems[i] = input[i];
        }

        return systems;
    }

    /**
     * @return Pega caminho onde est�o os arquivos de entrada
     */
    public final String getSourcePath() {
        final String input = configuration.getString("sourcepath");

        return input;
    }

    /**
     * @return Pegar arquivo de saida no formato [arquivo]
     */
    public final String getOutPutFile() {
        final String input = configuration.getString("outputfile");

        return input;
    }

    /**
     * Retrona um array com os objetivos do problema. No fomato
     * [objetivo],[objetivo] � mono objetivo. Quando for [objetivo#objetivo] �
     * multi objetivo
     * @return Array com os objetivos
     */
    public final Objective[][] getObjectives() {
        final String[] input = configuration.getStringArray("objetivos");

        // caso nada venha definido, default � tratar como mono objetivo
        if (input.length == 0) {
            return new Objective[][] { Objective.values() };
        }

        final Objective[][] inputTypes = new Objective[input.length][];

        for (int i = 0; i < input.length; i++) {

            final String[] t = input[i].split("#");

            // caso tenha apenas um objetivo
            if (t.length == 1) {
                inputTypes[i] = new Objective[] { Objective.valueOf(input[i]) };
                // caso seja multi objetivo
            } else {
                inputTypes[i] = Objective.valueOf(t);
            }
        }

        return inputTypes;
    }

    /**
     * @return Array com os tipos de movimentos
     */
    public final TypeMovement[] getTypeMovements() {
        final String[] input = configuration.getStringArray("pso.movimentos");

        if (input.length == 0) {
            return TypeMovement.values();
        }

        final TypeMovement[] typeMovements = new TypeMovement[input.length];

        for (int i = 0; i < input.length; i++) {
            typeMovements[i] = TypeMovement.valueOf(input[i]);
        }

        return typeMovements;
    }

    /**
     * @return Array com os operadores
     */
    public final Operador[] getOperacao() {
        final String[] input = configuration
                .getStringArray("hillclimbing.operadores");

        if (input.length == 0) {
            return Operador.values();
        }

        final Operador[] operador = new Operador[input.length];

        for (int i = 0; i < input.length; i++) {
            operador[i] = Operador.valueOf(input[i]);
        }

        return operador;
    }

    /**
     * @return Buscadores a serem iniciados
     * @throws HeuristicsException
     *             Caso tenha ocorrido algum erro durante a instanciação
     */
    public final AbstractStarter<?>[] getEngineStarters()
            throws HeuristicsException {

        try {
            final String[] input = configuration.getStringArray("starters");

            if (input.length == 0) {
                throw new ClassNotFoundException(
                        "Especificar engines a serem usadas.");
            }

            final AbstractStarter<?>[] newInstances = new AbstractStarter<?>[input.length];

            for (int i = 0; i < input.length; i++) {
                Class<?> c = Class.forName(input[i]);

                newInstances[i] = (AbstractStarter<?>) c
                        .getConstructor(Configurator.class)
                        .newInstance(this);
            }

            return newInstances;
        } catch (Exception e) {
            throw new HeuristicsException(e);
        }
    }
}
